#pragma once
#pragma once
#pragma once
#include <memory>
#include <unordered_map>
#include <functional>

#include <unistd.h>
#include <poll.h>

#include "Socket.hpp"
#include "Server.hpp"
#include "ClientInfo.hpp"
#include "Log.hpp"
#include "Error.hpp"
#include "Protocol.hpp"

/*
    多路转接之 select
*/
//

// 动态增长来管理 用户关心的文件描述符
// 在达到阈值之前2倍增长，超过阈值线性增长
const int pfd_init = 5;
const int pfd_threshold = 20;
const int pfd_increase = 4;
const int pfd_default = 0;

// 所关心的文件描述符的事件，以及其对应的ip,port

struct connect_buf
{

    connect_buf()
    {
    }

    connect_buf(Client cli)
        : _cli(cli)
    {
    }

    Client _cli;
    std::string inbuf;  // 输入缓冲区，接收请求的缓冲区
    std::string outbuf; // 输出缓冲区，存放响应的缓冲区
};

typedef struct pollfd type_t;

// 先写出echo服务器
// 再添加其他复杂业务

// 回调函数，处理业务
using func_t = std::function<Response(const Request &)>;

class PollServer : public Server
{
public:
    using ptr = std::unique_ptr<PollServer>;
    PollServer()
    {
        InitServer();
        _events = new type_t[pfd_init];
        memset(_events,pfd_default,sizeof(type_t)*pfd_init);
        _events_sz = pfd_init;
    }

    PollServer(func_t cb)
        : _cb(cb)
    {
        InitServer();
        _events = new type_t[pfd_init];
        memset(_events,pfd_default,sizeof(type_t)*pfd_init);
        _events_sz = pfd_init;
    }

    // 此时SelectServer再进行accept时，不会被阻塞了。
    // 因为select告知我listenfd，有事件就绪了，此时
    // 我进行读取即可。

    bool AddSpace()
    {
        int newsz = _events_sz < pfd_threshold ? _events_sz * 2 : _events_sz + pfd_increase;
        type_t *tmp = new type_t[newsz];
        memset(tmp,pfd_default,sizeof(type_t)*newsz);
        std::copy(_events, _events + _events_sz, tmp);
        delete[] _events;
        _events = tmp;
        _events_sz = newsz;
        return true;
    }

    void AcceptHandler()
    {
        Client cli;
        int sock = _listensock.Accept(&cli._ip, &cli._port);
        // 添加进用户关心的文件描述符集合中
        int i = 0;
        // 找到_events中第一个没有被使用的位置
        while (i < _events_sz && _events[i].fd != pfd_default)
            i++;

        // 是否需要进行扩容
        if (i == _events_sz)
        {
            if (!AddSpace())
            {
                LOG(INFO, "select 关心的文件描述符已达上限");
                return;
            }
            LOG(INFO, "扩容成功");
        }
        // 到此处是 [i].fd == pfd_default
        _events[i].fd = sock;
        _events[i].events |= POLLIN;
        // 建立 fd-buf的映射关系
        connect_buf cnf(cli);
        _fd_buf[i] = cnf;
    }
    void IsEnableWriteEvent(type_t &event, bool flag)
    {
        if (flag == true)
            event.events |= POLLOUT;
        else
            event.events &= ~POLLOUT;
    }
    // 1.先测试echo 服务器
    // 2.再进行CacularServer
    void ReadHandler(type_t &event)
    {
        // 此时进行读，是不会被阻塞的，因为select通知我读事件已经就绪了
        // recv(int sockfd, void *buf, size_t len, int flags);
        char buf[1024];
        // -1：为\0预留空间
        int n = recv(event.fd, buf, sizeof(buf) - 1, 0);
        if (n == 0)
        {
            LOG(INFO, "对端[%s-%d] 关闭", _fd_buf[event.fd]._cli._ip.c_str(), _fd_buf[event.fd]._cli._port);
            memset(&event, 0, sizeof(event));
            event.fd = pfd_default;
        }
        else if (n > 0)
        {
            buf[n] = 0;
            // cout << "buf" << buf << endl;
            // 将接收过来的数据存放到输入缓冲区
            _fd_buf[event.fd].inbuf += buf;
            // cout << "GetMsg before:: inbuf" << inbuf << endl;

            // 边读取边解析
            std::string msg; // 报文
            if (Protocol::GetMsg(_fd_buf[event.fd].inbuf, &msg) == false)
            {
                // 表示缓冲区内部没有一份完整的报文
                return;
            }
            // cout << "GetMsg after:: inbuf" << inbuf << endl;
            // cout << "msg  " << msg << endl;

            // 获取到完整的报文
            // 获取有效载荷
            std::string body = Protocol::DelHeader(msg);
            // 反序列化
            Request req;
            req.DisSerialize(body);
            // 得到请求，处理请求,得到响应
            Response resp = _cb(req);
            // using callback = std::function<Response(const Request&)>;

            // 序列化
            std::string resp_str;
            resp.Serialize(&resp_str);
            // 添加报头
            Protocol::AddHeader(resp_str);
            // 在多路转接中写事件一般不关心
            // 因为大部分情况下，写事件是就绪的，对方的是有足够的缓冲区的
            // 如果写事件就绪，多路转接每次都会提醒我写事件就绪，白白浪费资源
            n = send(event.fd, resp_str.c_str(), resp_str.size(), 0);
            if (n == -1)
            {
                // 发送失败，我再打开对写事件的关心，并把未发送成功的数据加入到输出缓冲区中
                IsEnableWriteEvent(event, true);
                _fd_buf[event.fd].outbuf += resp_str;
                LOG(ERROR, "send fail %d-%s", errno, strerror(errno));
            }
            else
            {
                LOG(INFO, "发送成功 n = %d", n);
            }
        }
        else
        {
            LOG(INFO, "recv failed %d-%s", errno, strerror(errno));
        }
        cout << "-----------------------" << endl;
    }
    void WriteHandler(type_t &event)
    {
        // outbuf中存储的都是，处理好的响应报文
        // 我们直接全部发送
        int n = send(event.fd, _fd_buf[event.fd].outbuf.c_str(), _fd_buf[event.fd].outbuf.size(), 0);
        if (n > 0)
        {
            // 如果outbuf中的数据已经全部发送成功了
            // 则关闭对写事件的关心
            if (n == _fd_buf[event.fd].outbuf.size())
                IsEnableWriteEvent(event, false);
            // 发送成功，将数据从outbuf中删除
            _fd_buf[event.fd].outbuf.erase(0, n);
        }
    }

    void EvenHandler()
    {
        for (int i = 0; i < _events_sz; i++)
        {
            int fd = _events[i].fd;
            // 读事件就绪
            if (_events[i].revents & POLLIN)
            {
                if (fd == _listenfd)
                {
                    LOG(INFO, "新连接到来");
                    AcceptHandler();
                }
                else
                {
                    LOG(INFO, "%d-读事件就绪", _events[i].fd);
                    ReadHandler(_events[i]);
                }
            } // 写事件就绪
            else if (_events[i].revents & POLLOUT)
            {
                LOG(INFO, "%d-写事件就绪", i);
                WriteHandler(_events[i]);
            }
        }
    }

    void ShowFds()
    {
        cout << "select 关心的文件描述符: ";
        for (int i = 0; i < _events_sz; i++)
        {
            if (_events[i].fd != pfd_default)
                cout << _events[i].fd << " ";
        }
        cout << endl;
    }

    // 事件派发器
    // 让select 关心用户想要关心的文件描述符的读事件，写事件，异常事件
    // 如果该文件描述符上的某个事件就绪后，进行事件派发让它处理对应的事件
    void Dispatcher()
    {
        LOG(INFO, "select server is running...");
        // 1.让select关心监听套接字
        // 在网络编程中，对于listensock 建立连接时，是算作读事件！
        _listenfd = _listensock.Fd();
        _events[0].fd = _listenfd;
        _events[0].events = POLLIN;
        while (true)
        {
            int timeout = 2;
            // nfds是最大的文件描述符
            // int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
            ShowFds();
            int n = poll(_events, _events_sz, timeout);
            if (n == -1)
            {
                LOG(INFO, "select 失败");
                LOG(ERROR, "%d - %s", errno, strerror(errno));
            }
            else if (n > 0)
            {
                LOG(INFO,"有事件就绪了");
                EvenHandler();
            }
            else
            {
                // LOG(INFO,"没有事件就绪");
                // ShowFds();
            }
            sleep(1);
        }
    }

    virtual ~PollServer()
    {
        delete[] _events;
    }

private:
    // 这样管理太分散了
    // int *_fds;
    // int fd_max;
    // int _fds_sz;
    int _events_sz;
    int _listenfd;
    // std::unordered_map<int,Client> _clients;
    type_t *_events;
    std::unordered_map<int, connect_buf> _fd_buf;
    func_t _cb;
};